if(typeof(models)=='undefined')
	this.models = models = new Repository();

models.register({
	name : 'FriendFeed',
	ICON : "data:image/gif,GIF89a%10%00%10%00%A2%00%00%94%B8%E5%7F%AB%E2%84%AF%E4H%82%C9%B0%CC%EFW%8D%D0%93%BA%EBC~%C7!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%03Wx%BA%DCJ0J%F9%22%00%26%9AK%9E)L%11%80%8D1%1CCQ%9CJ%BA*%60a%CC%DF%20%D0%C62%E0_%80%0B%0B%99A08%F9%0C%01%14L(P%15n%9F%D7%81G%9D%91%AAT%92j%D6TAU%AC%830%A98%92%82%B3%24%C8%7C%00%A0%91%E2%03%DB%ED%A8%2F%12%00%3B",
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		var token = getCookies('friendfeed.com', 'AT')[0];
		if(!token)
			throw 'AUTH_FAILD';
		
		return doXHR('https://friendfeed.com/share/publish', {
			sendContent : {
				at  : token.value,
				url : ps.pageUrl,
				title : ps.page,
				image0 : ps.type == 'photo'? ps.itemUrl : '',
				comment : joinText([ps.body, ps.description], ' ', true),
			},
		});
	},
});


models.register({
	name : 'FFFFOUND',
	ICON : "data:image/gif,GIF89a%10%00%10%00%A2%02%00%00%00%00%FF%FF%FF%FF%FF%FFfff%99%99%99%CC%CC%CC%FF%00%99%FF%00%CC!%F9%04%01%00%00%02%00%2C%00%00%00%00%10%00%10%00%00%03I(C%DB%FCK%08%12%AA%BD%A1%009%B0%D7%12%E5%5D%A0R%1D%86e%A4%D96%05%EB%0A%C7-7%C7x%FD%E2%BC%DE%05%A8U%B0R%12%DDdD%D7%AFB%B3%141%2C%A7rDr%19%A9%A5Aa%CB%EDn%5D%80%B0x%3C%16%24%00%00%3B",
	URL : 'http://FFFFOUND.com/',
	
	getToken : function(){
		return doXHR(FFFFOUND.URL + 'bookmarklet.js').addCallback(function(res){
			return res.responseText.match(/token='(.*?)'/)[1];
		});
	},
	
	check : function(ps){
		return ps.type == 'photo';
	},
	
	post : function(ps){
		if(ps.pageUrl.match('^http://ffffound.com/')){
			var id = ps.itemUrl.split('/').pop().replace(/[_\.].+/, '');
			return this.iLoveThis(id)
		}
		
		return this.getToken().addCallback(function(token){
			return doXHR(FFFFOUND.URL + 'add_asset', {
				referrer : ps.pageUrl,
				queryString : {
					token   : token,
					url     : ps.itemUrl,
					referer : ps.pageUrl,
					title   : ps.item,
				},
			}).addCallback(function(res){
				if(res.responseText.match('(FAILED:|ERROR:) (.*?)</span>'))
					throw RegExp.$2;
				
				if(res.responseText.match('login'))
					throw 'AUTH_FAILD';
			});
		});
	},

	remove : function(id){
		// 200 {"success":false}
		return doXHR(FFFFOUND.URL + 'gateway/in/api/remove_asset', {
			referrer : FFFFOUND.URL,
			sendContent : {
				collection_id : id,
			},
		});
	},
	
	iLoveThis : function(id){
		return doXHR(FFFFOUND.URL + 'gateway/in/api/add_asset', {
			referrer : FFFFOUND.URL,
			sendContent : {
				collection_id : 'i'+id,
				inappropriate : false,
			},
		}).addCallback(function(res){
			// NOT_FOUND / EXISTS / AUTH_FAILD
			if(res.responseText.match(/"error":"(.*?)"/))
				throw RegExp.$1;
		});
	},
});

models.register({
	name : 'Amazon',
	
	getItem : function(asin){
		return doXHR('http://webservices.amazon.co.jp/onca/xml', {
			queryString : {
				Service        : 'AWSECommerceService',
				SubscriptionId : '0DCQFXHRBNT9GN9Z64R2',
				Operation      : 'ItemLookup',
				ResponseGroup  : 'Small,Images',
				ItemId         : asin,
			},
		}).addCallback(function(res){
			var xml = convertToXML(res.responseText);
			if(xml.Error.length())
				throw res;
			
			return new Amazon.Item(xml.Items.Item);
		});
	},
	
	normalizeUrl : function(asin){
		return  'http://amazon.co.jp/o/ASIN/' + asin + 
			(this.affiliateId ? '/' + this.affiliateId + '/ref=nosim' : '');
	},
	
	get affiliateId(){
		return getPref('amazonAffiliateId');
	},
	
	Item : function(item){
		return {
			get title(){
				return ''+item.ItemAttributes.Title;
			},
			get creators(){
				var creators = [];
				
				// '原著'以外
				for each(var creator in item.ItemAttributes.Creator.(@Role != '\u539F\u8457'))
					creators.push(''+creator);
				return creators;
			},
			get largestImage(){
				return this.largeImage || this.mediumImage || this.smallImage;
			},
			get largeImage(){
				return new Amazon.Image(item.LargeImage);
			},
			get mediumImage(){
				return new Amazon.Image(item.MediumImage);
			},
			get smallImage(){
				return new Amazon.Image(item.SmallImage);
			},
		}
	},
	
	Image : function(img){
		if(!img.length())
			return;
		
		return {
			get size(){
				return (''+img.name()).slice(0, -5).toLowerCase();
			},
			get url(){
				return ''+img.URL;
			},
			get width(){
				return 1*img.Width;
			},
			get height(){
				return 1*img.Height;
			},
		}
	},
});

models.register({
	name : 'Flickr',
	ICON : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%00%F9IDATx%DAb%FC%FF%FF%3F%03%25%00%20%80%98%18(%04%00%01D%B1%01%00%01D%B1%01%00%01%C4%08g%25%9F%B0%00%92%91%40%0C%0A%94%95%0Cs-%8E%23%2B%FC%CF%D0b%09%A4%C2%A1z%9632%D4%9C%00%89%03%04%10%23Ts%13%90%ACEu%1Bc%2B%C3l%F3%1A%88%E6%E6%16%A0%BEj4%CB%9B%81%86%D4%01%04%10%23P3%C8%E4c%98%9Ecd%10%FB%F6%D3%E0%D1%8A%23%8C%EC%7F%FF%9E%87X%8C%01%02%00%02%88%05H%C4%E0%F1%A2'%03%0E%9DP%E0%0D%10%40%14%07%22%40%00%81%0CX%82G~%3B%10o%C3%23%BF%15%20%80%98%A0%A1%DD%84E%B2%E5%D5R%BB%8B%1C%7F%AB.%02%D9%ADX%E4%9B%80%81%B8%11%20%80%90%A3%D1%17%EAgP4%EE%04%1A%BC%09-%1A%FD%80%94%3B4L%B6%035o%06%89%03%04%10%23%A5y%01%20%80(%0ED%80%00%A2%D8%00%80%00%A2%D8%00%80%00%03%00I%DC7e%DD%B0%0B%9F%00%00%00%00IEND%AEB%60%82",
	API_KEY : 'ecf21e55123e4b31afa8dd344def5cc5',
	
	check : function(ps){
		return ps.type == 'photo' && ps.pageUrl.match('^http://www.flickr.com/photos/');
	},
	
	post : function(ps){
		return this.addFavorite(ps.pageUrl.replace(/\/$/, '').split('/').pop());
	},
	
	callMethod : function(ps){
		return doXHR('http://flickr.com/services/rest/', {
			queryString : update({
				api_key        : this.API_KEY,
				nojsoncallback : 1,
				format         : 'json',
			}, ps),
		}).addCallback(function(res){
			eval('var json=' + res.responseText);
			if(json.stat!='ok')
				throw json.message;
			return json;
		});
	},
	
	callAuthMethod : function(ps){
		return this.getToken(ps.photo_id).addCallback(function(page){
			ps = update(update({
				nojsoncallback : 1,
				format         : 'json',
				src            : 'js',
				cb             : new Date().getTime(),
			}, page.token), ps);
			ps.api_sig = (page.secret + keys(ps).sort().map(function(key){
				return key + ps[key]
			}).join('')).md5();
			
			return doXHR('http://flickr.com/services/rest/', {
				sendContent : ps,
			});
		}).addCallback(function(res){
			eval('var json=' + res.responseText);
			if(json.stat!='ok')
				throw json.message;
			return json;
		});
	},
	
	getToken : function(id){
		return this.getInfo(id).addCallback(function(photo){
			return doXHR(photo.urls.url[0]._content);
		}).addCallback(function(res){
			var html = res.responseText;
			return {
				secret : html.extract(/global_flickr_secret[ =]+'(.*?)'/),
				token  : {
					api_key    : html.extract(/global_magisterLudi[ =]+'(.*?)'/),
					auth_hash  : html.extract(/global_auth_hash[ =]+'(.*?)'/),
					auth_token : html.extract(/global_auth_token[ =]+'(.*?)'/),
				},
			};
		});
	},
	
	addFavorite : function(id){
		return this.callAuthMethod({
			method   : 'flickr.favorites.add',
			photo_id : id,
		});
	},
	
	removeFavorite : function(id){
		return this.callAuthMethod({
			method   : 'flickr.favorites.remove',
			photo_id : id,
		});
	},
	
	getSizes : function(id){
		return this.callMethod({
			method   : 'flickr.photos.getSizes',
			photo_id : id,
		}).addCallback(function(json){
			return json.sizes.size;
		});
	},
	
	getInfo : function(id){
		return this.callMethod({
			method   : 'flickr.photos.getInfo',
			photo_id : id,
		}).addCallback(function(json){
			return json.photo;
		});
	},
});

models.register({
	name : 'WeHeartIt',
	ICON : "data:image/gif,GIF89a%10%00%10%00%91%00%00%FF%FF%FF%FF%00f%FF3%99%FF%CC%CC!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%022%84%8F%A9%BB%E39T%98A%80!h4%99V%3FY%03HRWY%3A(%19%AD%9E%05t.%3C%BA%01R%A3%0AV2%3C%05c%5C%04%82%8D%F0%60D%14%00%00%3B",
	URL : 'http://weheartit.com/',
	
	check : function(ps){
		return ps.type == 'photo';
	},
	
	post : function(ps){
		if(ps.pageUrl.match('^http://weheartit.com/'))
			return this.iHeartIt(ps.source.split('/').pop());
		
		return doXHR(WeHeartIt.URL + 'add.php', {
			referrer : ps.pageUrl,
			queryString : {
				via   : ps.pageUrl,
				title : ps.item,
				img   : ps.itemUrl,
			},
		}).addCallback(function(res){
			if(!res.responseText.match('logout'))
				throw 'AUTH_FAILD';
		});
	},
	
	iHeartIt : function(id){
		return doXHR(WeHeartIt.URL + 'inc_heartedby.php', {
			referrer : ps.pageUrl,
			queryString : {
				do    : 'heart',
				entry : id,
			},
		}).addCallback(function(res){
			if(!res.responseText.match('logout'))
				throw 'AUTH_FAILD';
		});
	},
});

models.register({
	name : '4u',
	ICON : "data:image/gif,GIF89a%10%00%10%00%80%00%00%00%00%00%FF%FF%FF!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%02%12%84%8F%A9%CB%ED%0F%A3%9C%B4%DA%14%B2%DE%3C%DC%0F%22%05%00%3B",
	URL : 'http://4u.straightline.jp/',
	
	check : function(ps){
		return ps.type == 'photo';
	},
	
	post : function(ps){
		if(ps.pageUrl.match('^http://4u.straightline.jp/image/'))
			return this.iLoveHer(ps);
		
		return doXHR(ForU.URL + 'power/manage/register', {
			referrer : ps.pageUrl,
			queryString : {
				site_title  : ps.page,
				site_url    : ps.pageUrl,
				alt         : ps.item,
				src         : ps.itemUrl,
				bookmarklet : 1,
			},
		}).addCallback(function(res){
			if(!res.responseText.match('logout'))
				throw 'AUTH_FAILD';
		});
	},

	iLoveHer : function(ps){
		// doXHR(ps.pageUrl)
		if(!ps.id)
			return;
		
		return doXHR(ForU.URL + 'user/manage/do_register', {
			referrer : ps.pageUrl,
			queryString : {
				src : id,
			},
		}).addCallback(function(res){
			if(res.channel.URI.asciiSpec.match('login')){
				throw 'AUTH_FAILD';
			}
		});
	},
});

models.register({
	name : 'Local',
	
	// Mark James
	// http://www.famfamfam.com/lab/icons/silk/
	ICON : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%02-IDAT8%CB%7DS%3Bl%13A%10%7D%BBw%B6%A58%A7%D8%8E%C37%80R%E3D%20%20%88%0A%05%04%02!ACI%87D%8B%A8h)%11%05%B6k%A2%D0%F1Q%A0%00%0A%94%06%A5%A4%E1%B0%1B%0CH(4%20%0B%24%DBI%94%A0%F3%7D%96%99%D9%B3EP%92%3D%CD%CE%DD%ED%CE%7Bovf%951%06%BB%0D%DF%F7%1FGQt%23%0CC%97%3C%C8%8B%F5%FB%7D%F6%CF%D4N%00%CDfs%8C%02%E6%0B%85%C2%F5%5Cn%04%01%05!I%20%BB)%A6X%2C%60i%E9-%DC%ED%82%1B%8D%C6%25%0E.%97%CB%07%F3%F9%3CV%BE%FF%00%B3'%89Ab%12%022%88%09%2C%08%02%B8%ADV%2B%88%E38K%26%9B%062%D9w%BB%DD%FF%25%A3%D3%E9%12P%82%0B%17%2F%CB%3F%97%83''%0F%A3%D7%5B%15v%C3%8Fa%95F%8C%3F%88%13%A5%E2%18%DE%BC~%85%A3%95%E3%C2%CEk%A2%80Q9%F8%EE%BCO%DB%14%94%A2%D9N%E2%D9%D6%D7%D6%F0%E0%D6)a%FC%E8%BF%07%AB%DD%7F%E5%9AU%C0R%99%957%16K%E3%D0ZC%91%E9%D4%F8%9D%01Lb%19%2B3'%E5%DD%A4g%A0Y%01%7D%13%80%86%E38%5BL%A7%9E%07%1F%1E3%FE%F6V%B0%D0%ABIZ%A2%20L%CB%A3%B4%82%E3%BACv%F1%9C%02y%5B9%CB%B8%F0%E5%116%D6%23Q!%00%3C%F1%81p%0A%CC8%B5gD%F2%B7C%E1%E7j%94*%B0%8Cw%C6%EF!)Y%40%26%17%00%9D%E6%C9%F6%ED%EB%F6%1D%E9hG%00l%2F%D8J%0D%15d2.%EE%DF%3C%26%E5%09%C3%08%9B%9B%810d%B3%19J%C3H%F7ie%D7b%8EL%CB(%0A(%AF%87%CB%CB%EF%CE%0E%1A%85%7C%C9%F3%BC)%AEN%BB%DD%0EH%5D%8ES%E3%B8%CA%F4%0C%8E%1C%3A%20%8A%F6%ED%9D%90f%DBr%17%EA%F5%FAm%02%B9%3A%7B%FA%CC9%CF%2B%E0%E5%8B%A7%7F%A8%E6O%E8%7Cf%E9%FF%F4%BF%5D%CAFk%9F%87%00%B5Zm%82J%F6kn%EE%3CF%3D%8F6%C4p%1DM%A0%D5%8Dj%B5%3A%BA%D3m%1D%5E%26%92%DE!%FB%B0%B8%F8%FC%C4%A0%FFS%C6O%BB%5D%F7%BF%86%F1%A08%14%93%AD%D7%00%00%00%00IEND%AEB%60%82",
	
	check : function(ps){
		return ps.type == 'photo';
	},
	
	post : function(ps){
		var uri = broad(createURI(ps.itemUrl));
		var fileName = validateFileName(uri.fileName);
		var file = getDownloadDir();
		file.append(fileName);
		
		for(var count = 2 ; file.exists() ; count++)
			file.leafName = fileName.replace(/(.*)\./, '$1('+count+').');
		
		return download(ps.itemUrl, file).addCallback(function(){
			if(AppInfo.OS == 'Darwin'){
				// FIXME: Tempディレクトリを使う
				var script = getDataDir();
				script.append('setcomment.scpt');
				
				putContents(script, [
					'set aFile to POSIX file ("' + file.path + '" as Unicode text)',
					'set cmtStr to ("' + ps.pageUrl + '" as Unicode text)',
					'tell application "Finder" to set comment of (file aFile) to cmtStr'
				].join('\n'), 'UTF-16');
				
				var process = new Process(new LocalFile('/usr/bin/osascript'));
				process.run(false, [script.path], 1);
			}
		});
	},
});

models.register({
	name : 'Twitter',
	ICON : "data:image/gif,GIF89a%10%00%10%00%C4%00%00%88%EC%F6%00%DB%ED%F3%FB%FC%DE%F9%FC%17%DF%EF%B6%F3%F9g%E7%F3%A3%F1%F8%E3%F9%FB%95%EE%F7%5D%E7%F3%F9%FC%FDu%E9%F5%C9%F6%FB%C2%F5%FA%EA%FA%FC%7D%EA%F5%F8%FB%FBL%E4%F1%FB%FD%FD%D6%F9%FC%FE%FD%FD9%E2%F0%FD%FE%FE%D3%F6%FA%08%DD%ED%23%E1%EF)%E2%F0%D9%F8%FB%00%DE%EE%FE%FE%FE%FF%FF%FF!%F9%04%01%00%00%1F%00%2C%00%00%00%00%10%00%10%00%00%05%8D%E0%F7M%82%10-b%AA~%5ED9%8Dp%AD%AA%F7%1C%C5%81%A0%B4%E8!%89%E0%20B%24Nj%08%40%02Ph8%9D%3B%D5%00%02%00%40%AEX%C8C%CA%E8z%BD%06J%CA%C3Q%18%CE%E83%80%C7%C2H%14%92%82%E0AG%1C%C7%18%8B%C4%D2%98x%FE35%18%1A%1B%04%07%15%88%15~5%02%1B%1D%1D%04%00%07%93%07%03%2B%15%00%01%8F%01%9C%9C%04%5B)%17%0B%0A%9A%8F%A6%01%0E%2B%13%15%09%1A%19%AF%19%01%16%02%3D%11%02%1C%B8%B8%02%3C!%00%3B",
	
	check : function(ps){
		return true;
	},
	
	getToken : function(){
		return doXHR('http://twitter.com/account/settings').addCallback(function(res){
			var html = res.responseText;
			if(html.indexOf('signin')!=-1)
				throw 'AUTH_FAILD';
			
			return {
				authenticity_token : html.extract(/authenticity_token.+value="(.+?)"/),
				siv                : html.extract(/logout\?siv=(.+?)"/),
			}
		});
	},
	
	post : function(ps){
		return Twitter.getToken().addCallback(function(token){
			token.status = joinText([ps.item, ps.itemUrl, ps.body, ps.description], ' ', true);
			return doXHR('http://twitter.com/status/update', update({
				sendContent : token,
			}));
		});
	},
	
	remove : function(id){
		return Twitter.getToken().addCallback(function(ps){
			ps._method = 'delete';
			return doXHR('http://twitter.com/status/destroy/' + id, {
				referrer : 'http://twitter.com/home',
				sendContent : ps,
			});
		});
	},
	
	addFavorite : function(id){
		return Twitter.getToken().addCallback(function(ps){
			return doXHR('http://twitter.com/favourings/create/' + id, {
				referrer : 'http://twitter.com/home',
				sendContent : ps,
			});
		});
	},
});

models.register({
	name : 'Google',
	ICON : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%03%3FIDATx%DAb%FC%FF%FF%3F%17%03%03%83%3A%10%F3%02%F1%7F%06%04%F8%0E%C4%7F%19P%01%0B%10%B3%031%13%10%FF%04%E2%DB%00%01%C4%084%40%1B%C8%10BR%FC%0F%88%7F_%BE%C2%60s%FE%DC%0FSaa61V%16%86%9F%97%AE%7D%FC%FB%F7%CF%9B%0F%D1%91%C2%ABdd%84%DE%02%D5%B0%82%2C%01%08%20%16(%E3%07%D4v%10%FE%BDy%CF%9F%AA%E5sO%2B%A9%A9%BF_%7D%E9%92%AC%ED%913%B2%BE%FF%7F%9D%BB%1B%10%F8%AA%E5%EF%3F%BB%F7%405%7F%A0%162%01%04%10%0BT%F3_%A8%E6%EF%CF%3F28%AEXv%3F%5C%40%F8DZVv%C8~N%0E%FE%B5y%F5%FF%85%8F%1F%107%13%15%FE%FD%5D%5EN%1Ad%3B%1B%CCO%00%01%04%F3%CBw%A8A%9F%9F%3D%60%D0%7F%F6%F0'%03%2B%3B%D371Q%C9%9F%BC%BC%7C_%D2%C3%F96p%B12%B2%EC%DA%7DV%E9%CB%97%0F%DF%91%D4%FF%00%08%20%26(%03.%F8%E8%F9%9F%C7%2F%3FI0%3C%7D(l%F1%E6%CD%937%40%B17%02%FC%8C_%BF%BC%BB%CE%F0%FD%EB%83%7B%7F%FE%FC%FB%8D%A4%FE%3B%40%00%C1%0C%80aF%13%DD%9F%2B%E4%85%9E%5D%BAsM'g%F9%F2%1F%F9%0F%9F%FE%F7%9C9%E3a%CA%F7_%9B%FBml%D4O%F2%F3%09%FC%84%BA%1A%AC%07%20%80%18%80%B1%C0%09%8AJ(%E6%06b%96%ED%3BN%CA%E8%98%D6%94%7Bxm%3ATV%FE%EF%BF%A2R%DD%DC%CC%9Cl%CE%8F%1F%3Fr!%A9%05c%80%00b%F9%F0%E1%C3%EF%7F%FF%FE%C1%23%FA%DF%BF%BF%FF%3C%3D%CC%9F%00%99%9Da%ED%5BO%9D%3B%CB%B0OT%C0%C3%8E%9B%F3%8E.%3F%3F%FF%A9w%EF%DE%B2%FCGJ-%00%01%C4hll%CC%F4%FA%F5k%06F%20x%F8%F0%E1_U%0DM%16c%E3%9Cd6%16k%E7%D7o%BE%B1%DD%7F%F4FEV%C8B%FB%FF%DF_%9F%1E%BE%2C%F5%BA%7D%7B%F9Qyyy%26%98%01%00%01%C4%20((%C8%02Ma%CC%0C%2Cl%1C%91%B9%3B%B6%D8%DB%EF%7C%C8%CD%E7%13%C3%CC%A6%AE%CA%C0%22%22%20%25%11%97%E9%EA%FC%E1%8F%BC%CA%BC%A3%C0%60%E2%00%AB%85%E8a%01%08%20%06qqq6h%F2d0%B7%0B%F3-%AD%FA%F3%9F%91%D5%3A%8D%81%89%91%09%AE%08%08%D4%F4%7B%3B%D5u%D6%BFe%60%E4%96EJ%D2%EC%00%01%C4%045%0D%84%19%DE%BCf%E5%BEy%87%99AF%C2B%93%E1%DF%FF%7F%D0%14%07%C2%0C%02%1CL%DC_%3E%9E%3D%CB%F0%FF%C7%0F%B8%8B%81%18%20%80X%FE%FE%FD%0B%E20%021%F7%BD%DB%3B%F6%F1%B0m%3E%A6%AATU%C0'n%AD%F4%E9%E3%81%1D%CC%FF~%FC%14%140%B4%F8%F6%8D%5D%FD%D5%AB%895%0C%8C%C0t%F0%1F%9C%FCAz%18%00%02%88QMM%8D%FF%D6%AD%5B%AC%D0%A4%FC%8B%91%89G%80_%D8'ZJ%CC%C5%91%89%5D%90%9B%E1%D7%BB%B7o%DE%9D8%FD%EA%F5%D6%0D%FF%FE%BC~%C1%F0%FF%EF%3F%A8fFvv%F6%7F%00%01%C4%B0u%EBVV%19%19%19Q%A0%80%14%14%8B%011%3F%84f%91%06%E6%17%90%988%10KB1X%1D%2F%2F%AFD__%1F'%40%80%01%00l%2BQRK%11W%82%00%00%00%00IEND%AEB%60%82",
});

// copied from http://userscripts.org/scripts/show/19741
models.register({
	name : 'GoogleWebHistory',
	ICON : models.Google.ICON,
	
	getCh : function(url){
		function r(x,y){
			return Math.floor((x/y-Math.floor(x/y))*y+.1);
		}
		function m(c){
			var i,j,s=[13,8,13,12,16,5,3,10,15];
			for(i=0;i<9;i+=1){
				j=c[r(i+2,3)];
				c[r(i,3)]=(c[r(i,3)]-c[r(i+1,3)]-j)^(r(i,3)==1?j<<s[i]:j>>>s[i]);
			}
		}
		
		return (this.getCh = function(url){
			url='info:'+url;
			
			var c = [0x9E3779B9,0x9E3779B9,0xE6359A60],i,j,k=0,l,f=Math.floor;
			for(l=url.length ; l>=12 ; l-=12){
				for(i=0 ; i<16 ; i+=1){
					j=k+i;c[f(i/4)]+=url.charCodeAt(j)<<(r(j,4)*8);
				}
				m(c);
				k+=12;
			}
			c[2]+=url.length;
			
			for(i=l;i>0;i--)
				c[f((i-1)/4)]+=url.charCodeAt(k+i-1)<<(r(i-1,4)+(i>8?1:0))*8;
			m(c);
			
			return'6'+c[2];
		})(url);
	},
	
	post : function(url){
		return doXHR('http://www.google.com/search?client=navclient-auto&ch=' + GoogleWebHistory.getCh(url) + '&features=Rank&q=info:' + escape(url));
	},
});

models.register({
	name : 'GoogleBookmarks',
	ICON : models.Google.ICON,
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		return doXHR('http://www.google.com/bookmarks/mark', {
			queryString :	{
				op : 'add',
			},
		}).addCallback(function(res){
			var doc = convertToHTMLDocument(res.responseText);
			if(doc.getElementById('gaia_loginform'))
				throw 'AUTH_FAILD';
			
			var fs = formContents(doc);
			return doXHR('http://www.google.com'+$x('//form[@name="add_bkmk_form"]/@action', doc), {
				sendContent  : {
					title      : ps.item,
					bkmk       : ps.itemUrl,
					annotation : joinText([ps.body, ps.description], ' ', true),
					labels     : ps.tags? ps.tags.join(',') : '',
					btnA       : fs.btnA,
					sig        : fs.sig,
				},
			});
		});
	},
});

models.register({
	name : 'Delicious',
	ICON : "data:image/gif,GIF89a%10%00%10%00%91%00%00%00%00%00%FF%FF%FF%DD%DD%DD%00%00%FF!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%02*%8Co%A3%AB%88%CC%DC%81K%26%3Al%C0%D9r%FDy%18%40%96%A4%80%A6%A8i%AA*%5B%BA)%7C%CA%02%0D%D87%AD%E3%3Do%2B%00%00%3B",
	getUserTags : function(user){
		return doXHR('http://feeds.delicious.com/feeds/json/tags/' + (user || Delicious.getCurrentUser())).addCallback(function(res){
			return reduce(function(memo, tag){
				memo.push({
					name      : tag[0],
					frequency : tag[1],
				});
				return memo;
			}, Components.utils.evalInSandbox(res.responseText, Components.utils.Sandbox('http://feeds.delicious.com/')), []);
		});
	},
	
	getCurrentUser : function(){
		if(decodeURIComponent(getCookieString('del.icio.us', '_user')).match(/user=(.*?) /))
			return RegExp.$1;
		
		throw 'AUTH_FAILD';
	},
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		return doXHR('http://del.icio.us/post/', {
			queryString :	{
				title : ps.item,
				url   : ps.itemUrl,
			},
		}).addCallback(function(res){
			var doc = convertToHTMLDocument(res.responseText);
			if(!doc.getElementById('delForm'))
				throw 'AUTH_FAILD';
			
			return doXHR('http://del.icio.us'+$x('id("delForm")/@action', doc), {
				sendContent : update(formContents(doc), {
					description : ps.item,
					jump        : 'no',
					notes       : joinText([ps.body, ps.description], ' ', true),
					tags        : ps.tags? ps.tags.join(' ') : '',
					private     : ps.private? 'on' : '',
				}),
			});
		});
	},
});

models.register({
	name : 'Instapaper',
	ICON : "data:image/gif,GIF89a%10%00%10%00%B3%00%00%D2%D2%D2%DB%DB%DB333%C2%C2%C2%BB%BB%BB%AA%AA%AA%98%98%98%A5%A5%A5%EB%EB%EB%F4%F4%F4hhh%CA%CA%CA%E3%E3%E3%B0%B0%B0%00%00%00%FF%FF%FF!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%04%85%F0%C9I%AB%BD8%EB%BCD%40%84%02%00%01%13%04%23%80%0CB%EB(N%EC%C6%CE%F14%C31%24%CD%A2%0C%00%05a%F4h%19%8F%C8%96%81%C0%04%10%16%83%C0%60%01%18%10%06%40%14%02%F0%60%00x%88S%22%11(%40%07%86C%C0%B0x2%0A%07%849J-%90%E0%8CF%EF%B0h%18%02Om%5E%00%0CW%00%07_%07%06%3C%05%0D%07%88f%0D%01%88%23%04'%2B%0B%06%08%8AV%05%9BW%04%08%06S%0B%07%0C%3AR%06%11%00%3B",
	
	check : function(ps){
		return ps.type == 'link' || ps.type == 'quote';
	},
	
	post : function(ps){
		return doXHR('http://www.instapaper.com/edit', {
			sendContent : {
				'bookmark[title]' : ps.item, 
				'bookmark[url]' : ps.itemUrl,
				'bookmark[selection]' : joinText([ps.body, ps.description], '\n', true),
			},
		}).addCallback(function(res){
			if(res.channel.URI.asciiSpec.match('login')){
				throw 'AUTH_FAILD';
			}
		});
	},
});


// http://www.kawa.net/works/ajax/romanize/japanese.html
models.register({
	name : 'Kawa',
	
	getReadings : function(text){
		return doXHR('http://www.kawa.net/works/ajax/romanize/romanize.cgi', {
			queryString : {
				mode : 'japanese',
				q : text,
			},
		}).addCallback(function(res){
			return map(function(s){	
				return ''+s.@title || ''+s;
			}, convertToXML(res.responseText).li.span);
		});
	},
});


// http://developer.yahoo.co.jp/jlp/MAService/V1/parse.html
models.register({
	name : 'Yahoo',
	APP_ID : '16y9Ex6xg64GBDD.tmwF.WIdXURG0iTT25NUQ72RLF_Jzt2_MfXDDZfKehYkX6dPZqk-',
	
	parse : function(ps){
		ps.appid = this.APP_ID;
		return doXHR('http://api.jlp.yahoo.co.jp/MAService/V1/parse', {queryString : ps}).addCallback(function(res){
			return convertToXML(res.responseText);
		});
	},
	
	getReadings : function(str){
		return this.parse({
			sentence : str,
			response : 'reading',
		}).addCallback(function(res){
			return list(res.ma_result.word_list.word.reading);
		});
	},
});


models.register({
	name : 'YahooBookmarks',
	ICON : "data:image/gif,GIF89a%10%00%10%00%A2%07%00%C2%D4%DD%FF%F6%F5%FF%BA%AD%FF%87%7B%8D%B2%CDa%94%B6%E7TN%FF%FF%FF!%F9%04%01%00%00%07%00%2C%00%00%00%00%10%00%10%00%00%03Px'%DC%FAP%84I%05%7C%92Mmo%93%C1w-%20%15v%D0%60%0C%A6%3A%00%F0c%CC%E6lP%80%22%D0%03%3BK%B0%40%EE%A0%B2%D9%26%B0%98%CEXB%0E!%85%02%C1D%88%8E%A2%05%13v%04%88%06%08%D3%E8%13R%C5F%09%A3G%17%3BN%BB%13%00%3B",
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		return doXHR('http://bookmarks.yahoo.co.jp/action/post').addCallback(function(res){
			if(res.responseText.indexOf('login_form')!=-1)
				throw 'AUTH_FAILD';
			
			return formContents($x('(id("addbookmark")//form)[1]', convertToHTMLDocument(res.responseText)));
		}).addCallback(function(fs){
			return doXHR('http://bookmarks.yahoo.co.jp/action/post/done', {
				sendContent  : {
					title      : ps.item,
					url        : ps.itemUrl,
					desc       : joinText([ps.body, ps.description], ' ', true),
					tags       : ps.tags? ps.tags.join(' ') : '',
					crumbs     : fs.crumbs,
					visibility : ps.private==null? fs.visibility : (ps.private? 0 : 1),
				},
			});
		});
	},
	
	getUserTags : function(){
		return doXHR('http://bookmarks.yahoo.co.jp/bookmarklet/showpopup').addCallback(function(res){
			if(res.responseText.match(/yourtags =(.*)(;|$)/)[1]){
				return reduce(function(memo, tag){
					memo.push({
						name      : tag,
						frequency : -1,
					});
					return memo;
				}, Components.utils.evalInSandbox(RegExp.$1, Components.utils.Sandbox('http://bookmarks.yahoo.co.jp/')), []);
			}
			
			throw 'AUTH_FAILD';
		});
	},
});

models.register({
	name : 'Hatena',
	ICON : "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%00%DBIDATx%DAb%7C%FD%E1%E7%7F%06(%F8%FA%93%91a%CD%CE%2F0.%C3%B1%EB%3F%19%AC4%D9Q%F8%EB%DA%25%19%19%90%00%40%00%B1%C04%82%C0%F3%97%AF%80%24%17X!%08%DCx%F6%08h%80*%0A%1F%1D%00%04%10%13%03%85%00%20%80X%40%B6ClF8%13%D9%26t%3E%3A%00%08%20F%F40%E0f%87s%B1%F2%D1%C3%08%20%80%98%60%12%C8.!%C4%07i%84%B9%0C%20%80(%0E%03%80%00%C2%08%03B%7C%F40%01%08%20Ft%13IM%17%00%01%C4%82%CDY%A4%A4%0B%80%00%A28%0C%00%02%88%05%9B%ED%A4%A4%0B%80%00b%E9Y%F4%EE%3F%B2%A4%828%1B%23f%98%08%A1%A5%0B%04%1F%20%80X%60%1Aq%A5u%F40Q%91%13E%E1%03%04%10%C5a%00%10%40%2C%84%D2%3A%A1t%01%10%60%00%D6%C3%C6%09%902m%ED%00%00%00%00IEND%AEB%60%82",
	
	getPasswords : function(){
		return getPasswords('https://www.hatena.ne.jp');
	},
	
	login : function(user, password){
		var self = this;
		return (this.getAuthCookie()? this.logout() : succeed()).addCallback(function(){
			return doXHR('https://www.hatena.ne.jp/login', {
				sendContent : {
					name : user,
					password : password,
					persistent : 1,
					location : 'http://www.hatena.ne.jp/',
				},
			});
		}).addCallback(function(){
			self.cookie = self.getAuthCookie();
			self.user = user;
		});
	},
	
	logout : function(){
		return doXHR('http://www.hatena.ne.jp/logout');
	},
	
	getAuthCookie : function(){
		return getCookieString('.hatena.ne.jp', 'rk');
	},
	
	getCurrentUser : function(){
		var self = this;
		var cookie = this.getAuthCookie();
		if(!cookie){
			return succeed('');
		} else if(self.cookie == cookie){
			return succeed(self.user);
		}
		
		return doXHR('http://www.hatena.ne.jp/my').addCallback(function(res){
			self.cookie = cookie;
			return self.user = $x(
				'(//*[@class="username"]//strong)[1]/text()', 
				convertToHTMLDocument(res.responseText));
		});
	},
});

models.register({
	name : 'HatenaBookmark',
	ICON : "data:image/gif,GIF89a%10%00%10%00%A2%04%00%84%9A%E7%10%24Z%86%A4%F8%18A%CE%FF%FF%FF%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%04%00%2C%00%00%00%00%10%00%10%00%00%03%3BH%BA%DC%0A0%CA8%DE%B88%DF%60%B5%E7%04%80%09%24%99%81%E2e%0E%EB%80%8E%82%1Ao%1D%5B%B6%AF%0C%D3%E1%AE%BB%B5%D6*g%BBa%88%9Ec-%C9s%05%9E%D0%E8%D3Ae%24%00%00%3B",
	POST_URL : 'http://b.hatena.ne.jp/add',
	
	getUserTags : function(){
		return doXHR(HatenaBookmark.POST_URL+'?mode=confirm').addCallback(function(res){
			if(res.responseText.match(/var tags ?=(.*);/)){
				return reduce(function(memo, tag){
					memo.push({
						name      : tag,
						frequency : -1,
					});
					return memo;
				}, Components.utils.evalInSandbox(RegExp.$1, Components.utils.Sandbox('http://b.hatena.ne.jp/')), []);
			}
			
			throw 'AUTH_FAILD';
		});
	},
	
	getToken : function(){
		return doXHR(HatenaBookmark.POST_URL).addCallback(function(res){
			if(res.responseText.match(/Hatena\.rkm\s*=\s*['"](.+?)['"]/))
				return RegExp.$1;
			
			throw 'AUTH_FAILD';
		});
	},
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		return HatenaBookmark.getToken().addCallback(function(token){
			var content = {
				mode    : 'enter',
				rkm     : token,
				url     : ps.itemUrl,
				comment : ((ps.tags && ps.tags.length)? '[' + ps.tags.join('][') + ']' : '') + joinText([ps.body, ps.description], ' ', true),
			};
			if(ps.item)
				content.title = ps.item;
			return doXHR(HatenaBookmark.POST_URL, {
				sendContent : content,
			});
		});
	},
});

models.register({
	name : 'HatenaStar',
	ICON : "data:image/gif,GIF89a%10%00%10%00%A2%04%00*_%C9%85%A5%FA%FF%FF%FFRy%E7%FF%FF%FF%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%04%00%2C%00%00%00%00%10%00%10%00%00%039H%BA%DC%1A0%CA8%DE%B88_%60%B5%E7D%90%09%A4%90%81%E2E%AA%E6%D6%0D%EB%F7%C21%86z%F5p%E3%AD%FE%C6%B5%1D%2B'%F4%D8fF%1FA%07h%3A%9FM%87%94%91%00%00%3B",
	
	getToken : function(){
		return doXHR('http://s.hatena.ne.jp/entries.json').addCallback(function(res){
			if(!res.responseText.match(/"rks":"(.*?)"/))
				throw 'AUTH_FAILD'
			return RegExp.$1;
		})
	},
	
	check : function(ps){
		return ps.type != 'regular';
	},
	
	post : function(ps){
		return HatenaStar.getToken().addCallback(function(token){
			return doXHR('http://s.hatena.ne.jp/star.add.json', {
				queryString :	{
					rks      : token,
					title    : ps.item,
					quote    : joinText([ps.body, ps.description], ' ', true),
					location : ps.pageUrl,
					uri      : ps.itemUrl,
				},
			});
		});
	},
	
	remove : function(ps){
		return HatenaStar.getToken().addCallback(function(token){
			return doXHR('http://s.hatena.ne.jp/star.delete.json', {
				queryString :	{
					rks   : token,
					uri   : ps.itemUrl,
					quote : joinText([ps.body, ps.description], ' ', true),
				},
			});
		});
	},
});


models.register({
	name : 'Wassr',
	ICON : "data:image/gif,GIF89a%10%00%10%00%C4%00%00%F8%FC%F6E%B8%0B%AC%DF%94u%CBI%E8%F6%E37%AA%08%A4%D3%97%C6%E7%BA%40%B2%0BH%BC%0Cc%B6K%8E%CA%7DE%B1%14%2B%A3%03%B8%E6%A0%D1%EE%C2g%C76%3F%B0%0A%20%94%00%9B%D8~U%C3%1Dx%BCf%DF%EF%DB%5C%BC4T%AD%3CD%A4*%23%9B%00%1F%92%00%92%D9m%22%97%00B%B5%0B%FF%FF%FF!%F9%04%00%00%00%00%00%2C%00%00%00%00%10%00%10%00%00%05p%E0'%8Edi%9E%24%C1%AD%1C%A0r%CE%E7%B01%95%DC%8FpS%1Ft'1%CEo2%F8%3D~%09%82%2C%C0%2C2%03%83'DD%F0X%AFX%EBdt%89x%BF%88p%F80Zh4%8Dta%EDe%00F%87%B3%7C%AEQ%90%00%9A%8E%BE%A3%D8w%0C%25%0A%12%83%18%02%83%83J%24%06%1B%8C%0B%04%8C%1B%19%26%16%15%95%16%1F%0B%95%80(%9C%9D'!%00%3B",
	
	check : function(ps){
		return true;
	},
	
	post : function(ps){
		return doXHR('http://wassr.jp/my/').addCallback(function(res){
			return doXHR('http://wassr.jp/my/status/add', {
				sendContent : update(formContents(convertToHTMLDocument(res.responseText)), {
					message : joinText([ps.item, ps.itemUrl, ps.body, ps.description], ' ', true),
				}),
			});
		})
	},
});

models.copyTo(this);

